<?php
class Connection
{
	const E_BUFFER_OVERWLOW = 1001;
	const E_TIMEOUT = 1002;

	const BUFFER_MAX_SIZE = 65536;
	public $_sock = null;
	private $_readBuffer = '';
	private $_writeBuffer = '';
	private $_timeout = 60;
	private $_lastAccessDate = null;

	private $_closed = false;

	public $server = null;

	function __construct($sock) {
		$this->_sock = $sock;
	}

	public function _setServer(SocketServer $server) {
		$this->server = $server;
	}

	public function onAccept() {
		$this->send(" == Connected\n");
	}

	public function onClose() {
	}

	public function onError($error) {
	}

	public function onDataReceived($data) {
		return 0;
	}

	public function _isClosed() {
		return $this->_closed;
	}
	
	public function _error($error) {
		Log::warning("Socket error: $error");
		$this->onError($error);
		$this->_close();
		$this->onClose();
	}

	public function send($data) {
		$this->_writeBuffer .= (string)$data;
		/**
		 * @todo проверка на переполнение буфера
		 */
	}

	public function _recv() {
		$this->_lastAccessDate = microtime(true);

		$data = socket_read($this->_sock, 2048);
		if(strlen($data)) {
			$this->_readBuffer .= $data;
			$readed = $this->onDataReceived($this->_readBuffer);
			if($readed)
				$this->_readBuffer = substr($this->_readBuffer, $readed);

			if(strlen($this->_readBuffer) > self::BUFFER_MAX_SIZE)
				$this->_error(self::E_BUFFER_OVERWLOW);

		} else {
			$this->_close();
			$this->onClose();
		}
	}

	public function _send() {
		$this->_lastAccessDate = microtime(true);
		
		$written = socket_send($this->_sock, $this->_writeBuffer, strlen($this->_writeBuffer), 0);
		
		if($written === false) {
			$this->_error(socket_last_error());
		} else {
			if($written)
				$this->_writeBuffer = substr($this->_writeBuffer, $written);
		}
	}

	public function _close() {
		socket_close($this->_sock);
		$this->_closed = true;
	}

	public function checkTimeout() {
		if(is_null($this->_lastAccessDate))
			return false;

		if(microtime(true) - $this->_lastAccessDate > $this->_timeout) {
			Log::notice('Timed out');
			$this->_error(self::E_TIMEOUT);
			return true;
		}

		return false;
	}

	public function _needRecv() {
		return (bool)strlen($this->_writeBuffer);
	}
}
